package tools.biback

//import cats.instances.vector._
import java.text.SimpleDateFormat
import java.util.{Calendar, GregorianCalendar}

import biback.utils.{DomainObject, InvalidCommand}
import cats._
import cats.data._
import cats.implicits._
//import cats.syntax.writer
//import cats.syntax.applicative._
//import cats.implicits._

object TryWriter extends App {
  type Logged = Writer[Vector[String], Int]

  val x = for {
    a <- Writer.value[Vector[String], Int](1)
    _ <- Vector("a", "b").tell
    b <- Writer.value[Vector[String], Int](3)
    _ <- Vector("1", "2").tell
  } yield a + b
  println(x)
  val y = for {
    a <- Ior.both(Vector("begin"), 1)
    b <- Ior.right(a + 1)
    _ <- Ior.left(Vector("error"))
    c <- Ior.both(Vector("sqrt"), b * b)
  } yield c
  println(y)
  def program[F[_]: Monad](s: Console[F] with XLogger[F]) =
    for {
      _ <- s.console.putStrLn("input ...")
      h <- s.console.getStrLn
      _ <- s.logger.debug(h)
    } yield ()
  //program(Console.Mocker)
  program(MockXY)
//  println(Reading.rule(false))
//  println(Reading.rule(true))
//  println(Reading.run1().run(new R1Setter {
//    val r1 = "Hello"
//  }))
//  println(Reading.run1().run(new R1Setter {
//    val r1 = ""
//  }))
//  println(Reading.run2().run(new R1Setter with R2Setter {
//    val r1 = "Hello"
//    val r2 = 11
//  }))
//  println(Reading.run2().run(new R1Setter with R2Setter {
//    val r1 = ""
//    val r2 = 1
//  }))
val s0 = new R1Setter {
  val r1 = ""
}
  val s1 = new R1Setter with R2Setter {
  val r1 = ""
  val r2 = 1
}
  val s2 = new R1Setter with R2Setter {
    val r1 = "Hi"
    val r2 = 1
  }
  val s3 = new R1Setter with R2Setter {
    val r1 = "Hi"
    val r2 = 11
  }
  println(ReadingChild.run1().run(s1))
  println(ReadingChild.run1().run(s2))
  println(ReadingChild.run1().run(s3))
  println(ReadingChild.parentCheck().run(s1))
  println(ReadingChild.parentCheck().run(s2))
  println(ReadingChild.childCheck().run(s1))
  println(ReadingChild.childCheck().run(s3))

  /**
    * 使用Luhn算法校验的步骤：
    *
    * 从右边第1个数字（校验数字）开始偶数位乘以2；
    * 把步骤1种获得的乘积的各位数字与原号码中未乘2的各位数字相加；
    * 如果步骤2得到的总和模10为0，则校验通过。
    *
    * @param cert1 凭证号
    * @return
    */
  def calcCheckNum(cert1: String): Int = {
    val certR = (cert1 + "0").reverse
    val (indexEven, indexOdd) = (0 until certR.length).partition(_ % 2 == 0)
    val c2n: Char => Int = _ - '0'
    val n2n: Int => Int = _.toString.map(c2n).sum
    val numEven = indexEven.map(certR.charAt).map(c2n)
    val numOdd = indexOdd.map(certR.charAt).map(c2n).map(_ * 2).map(n2n)
    10 - (numEven ++ numOdd).sum % 10
  }
  println(calcCheckNum("622576000821952"))
  println(calcCheckNum("622609010761810"))
  println("----")
  println(calaDays("20190101", "20190102"))
  println(calaDays("20190101", "20190212"))
  println(calaDays("20180101", "20190212"))
  println(calaDays("20160101", "20170212"))
  def calaDays(a: String, b: String): Int = {
    val sdf = new SimpleDateFormat("yyyyMMdd")
    val da = sdf.parse(a)
    val db = sdf.parse(b)
    val ga = new GregorianCalendar()
    ga.setTime(da)
    val gb = new GregorianCalendar()
    gb.setTime(db)
      val day1 = ga.get(Calendar.DAY_OF_YEAR)
      val day2 = gb.get(Calendar.DAY_OF_YEAR)
      val year1 = ga.get(Calendar.YEAR)
      val year2 = gb.get(Calendar.YEAR)
      if (year1 != year2) { // 不同年
        (year1 until year2).map { i => // 闰年？
          if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) 366 else 365
        }.sum + (day2 - day1)
      }
      else day2 - day1
  }
}

trait Console[F[_]] {
  def console: Console.Service[F]
}
object Console {
  trait Service[F[_]] {
    def putStrLn(line: String): F[Unit]
    val getStrLn: F[String]
  }
  object Mocker extends Service[Id] {
    def putStrLn(line: String): Id[Unit] =
      println(line)
    val getStrLn: Id[String] =
      "hello"
  }
}

trait XLogger[F[_]] {
  def logger: XLogger.Service[F]
}
object XLogger {
  trait Service[F[_]] {
    def debug(line: String): F[Unit]
  }
  object Mocker extends Service[Id] {
    def debug(line: String): Id[Unit] =
      println(s"DEBUG: $line")
  }
}

object MockXY extends Console[Id] with XLogger[Id] {
  def console: Console.Service[Id] = Console.Mocker
  def logger: XLogger.Service[Id] = XLogger.Mocker
}

trait R1Setter {
  val r1: String
}

trait R2Setter {
  val r2: Int
}

object Reading extends DomainObject {
  type SetterType = R1Setter with R2Setter
  val r1: Reader[R1Setter, String] = Reader(_.r1)
  val r21: Reader[SetterType, String] = Reader(_.r1)
  val r22: Reader[SetterType, Int] = Reader(_.r2)
  def run2(): Reader[SetterType, ErrOrNone] =
    for {
      r1v <- r21
      r2v <- r22
    } yield rule(r1v.nonEmpty && r2v > 10)
  def run1(): Reader[R1Setter, ErrOrNone] =
    for {
      r1v <- r1
    } yield rule(r1v.nonEmpty)
  def rule(b: Boolean): ErrOrNone =
    for {
      x <- b ?= InvalidCommand(b)
    } yield x
}

trait ReadingParent extends DomainObject {
  val r1: Reader[Any, String] = Reader(_.asInstanceOf[R1Setter].r1)
  val r11: Reader[R1Setter, String] = Reader(_.r1)
  def run1(): Reader[Any, ErrOrNone] =
    for {
      r1v <- r1
    } yield rule(r1v)
  def rule(v: String): ErrOrNone =
    for {
      x <- v.nonEmpty ?= InvalidCommand(stringArgs(v))
    } yield x
  def parentCheck(): Reader[R1Setter, ErrOrNone] =
    for {
      r1v <- r11
    } yield rule(r1v)
}

object ReadingChild extends ReadingParent {
  type SetterType = R1Setter with R2Setter
  val r21: Reader[SetterType, String] = Reader(_.r1)
  val r22: Reader[SetterType, Int] = Reader(_.r2)
  val r2: Reader[Any, Int] = Reader(_.asInstanceOf[R2Setter].r2)
  val r1x: Reader[SetterType, R1Setter] = Reader(_.asInstanceOf[R1Setter])

  override def run1(): Reader[Any, ErrOrNone] =
    for {
      r1x <- super.run1()
      r2v <- r2
    } yield for {
      s <- r1x
      x <- rule(r2v)
    } yield x

  def rule(v: Int): ErrOrNone =
    for {
      x <- v > 10 ?= InvalidCommand(stringArgs(v))
    } yield x

  def childCheck(): Reader[SetterType, ErrOrNone] =
    for {
      r1x <- parentCheck().compose(r1x)
      r2v <- r22
    } yield for {
      s <- r1x
      x <- rule(r2v)
    } yield x
}